#include "gplib.h"
#include <stdio.h>

extern const int g_nByteOffset[4];

/**
 * Проверка координат прямоугольника на корректность
 * @param указатель на графический контекст
 * @param координата по X левой стороны прямоугольника
 * @param координата по Y верхней стороны прямоугольника
 * @param координата по X правой стороны прямоугольника
 * @param координата по Y нижней стороны прямоугольника
 * @param имя цункции вызвавшей проверку
 * @return код ошибки
 */
inline int RectAssert(TGraphicContext *ctx, int x1, int y1, int x2, int y2, char *pszFunc) {
	if (ctx == NULL) {
		debug(("%s *Error: ctx == NULL\n", pszFunc));
		return GP_ERR;
	}
	if (x1 < 0) {
		debug(("%s *Error: x1 < 0, x1 = %d\n", pszFunc, x1));
		return GP_ERR;
	}
	if (x1 >= ctx->nScreenSizeX) {
		debug(("%s *Error: x1 >= nScreenSizeX, x1 = %d\n", pszFunc, x1));
		return GP_ERR;
	}
	if (x2 < 0) {
		debug(("%s *Error: x2 < 0, x2 = %d\n", pszFunc, x2));
		return GP_ERR;
	}
	if (x2 >= ctx->nScreenSizeX) {
		debug(("%s *Error: x2 >= nScreenSizeX, x2 = %d\n", pszFunc, x2));
		return GP_ERR;
	}
	if (y1 < 0) {
		debug(("%s *Error: y1 < 0, y1 = %d\n", pszFunc, y1));
		return GP_ERR;
	}
	if (y1 >= ctx->nScreenSizeY) {
		debug(("%s *Error: y1 >= nScreenSizeY, y1 = %d\n", pszFunc, y1));
		return GP_ERR;
	}
	if (y2 < 0) {
		debug(("%s *Error: y2 < 0, y2 = %d\n", pszFunc, y2));
		return GP_ERR;
	}
	if (y2 >= ctx->nScreenSizeY) {
		debug(("%s *Error: y2 >= nScreenSizeY, y2 = %d\n", pszFunc, y2));
		return GP_ERR;
	}

	return GP_OK;
}

#define SWAP_IF_NEED()	\
	int nSwap;			\
						\
	if (x1 > x2) {		\
		nSwap = x1;		\
		x1 = x2;		\
		x2 = nSwap;		\
	}					\
	if (y1 > y2) {		\
		nSwap = y1;		\
		y1 = y2;		\
		y2 = nSwap;		\
	}

/**
 * Рисование прямоугольника линией единичной толщины
 * @param указатель на графический контекст
 * @param координата по X левой стороны прямоугольника
 * @param координата по Y верхней стороны прямоугольника
 * @param координата по X правой стороны прямоугольника
 * @param координата по Y нижней стороны прямоугольника
 * @param цвет
 * @return код ошибки
 */
int Rectangle(TGraphicContext *ctx, int x1 ,int y1, int x2, int y2, TColor Color) {
	SWAP_IF_NEED()

	char bReorder = ctx->bReorder;
	int nBytesPerLine = ctx->nBytesPerLine;
	int nBytesPerPixel = ctx->nBitsPerPixel / 8;
	int nCnts = (x2 - x1) * nBytesPerPixel;
	int nCnt = nCnts + nBytesPerPixel;
	char *pScrPtr, *pLastPtr, *i;

	if (RectAssert(ctx, x1, y1, x2, y2, "Rectangle") != GP_OK) {
		return GP_ERR;
	}

	switch (ctx->nBitsPerPixel) {
	case 8:
		pScrPtr = GET_PIXEL_PTR_8(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i++) {
			SET_PIXEL_8(i, &Color)
		}
		pScrPtr += nBytesPerLine;

		for (y1++; y1 < y2; y1++) {
			SET_PIXEL_8(pScrPtr, &Color)
			SET_PIXEL_8(pScrPtr + nCnts, &Color)
			pScrPtr += nBytesPerLine;
		}
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i++) {
			SET_PIXEL_8(i, &Color)
		}
		break;
	case 16:
		pScrPtr = GET_PIXEL_PTR_16(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
			SET_PIXEL_16(i, &Color)
		}
		pScrPtr += nBytesPerLine;

		for (y1++; y1 < y2; y1++) {
			SET_PIXEL_16(pScrPtr, &Color)
		SET_PIXEL_16(pScrPtr + nCnts, &Color)
			pScrPtr += nBytesPerLine;
		}
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
			SET_PIXEL_16(i, &Color)
		}
		break;
	case 24:
		pScrPtr = GET_PIXEL_PTR_24(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
			SET_PIXEL_24(i, &Color)
		}
		pScrPtr += nBytesPerLine;

		for (y1++; y1 < y2; y1++) {
			SET_PIXEL_24(pScrPtr, &Color)
			SET_PIXEL_24(pScrPtr + nCnts, &Color)
			pScrPtr += nBytesPerLine;
		}
		pLastPtr = pScrPtr + nCnt;

		for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
			SET_PIXEL_24(i, &Color)
		}
		break;
	}

	return GP_OK;
}

/**
 * Рисование закрашенного прямоугольника
 * @param указатель на графический контекст
 * @param координата по X левой стороны прямоугольника
 * @param координата по Y верхней стороны прямоугольника
 * @param координата по X правой стороны прямоугольника
 * @param координата по Y нижней стороны прямоугольника
 * @param цвет
 * @return код ошибки
 */
int FillRectangle(TGraphicContext *ctx, int x1, int y1, int x2, int y2, TColor Color) {
	SWAP_IF_NEED()

	char bReorder = ctx->bReorder;
	int nBytesPerLine = ctx->nBytesPerLine;
	int nBytesPerPixel = ctx->nBitsPerPixel / 8;
	int nCnt = (x2 - x1 + 1) * nBytesPerPixel;
	char *pScrPtr, *pLastPtr, *i;

	if (RectAssert(ctx, x1, y1, x2, y2, "FillRectangle") != GP_OK) {
		return GP_ERR;
	}

	switch (ctx->nBitsPerPixel) {
	case 8:
		pScrPtr = GET_PIXEL_PTR_8(ctx->pData, x1, y1, nBytesPerLine);

		for (; y1 <= y2; y1++) {
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i++) {
				SET_PIXEL_8(i, &Color)
			}
			pScrPtr += nBytesPerLine;
		}
		break;
	case 16:
		pScrPtr = GET_PIXEL_PTR_16(ctx->pData, x1, y1, nBytesPerLine);

		for (; y1 <= y2; y1++) {
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_16(i, &Color)
			}
			pScrPtr += nBytesPerLine;
		}
		break;
	case 24:
		pScrPtr = GET_PIXEL_PTR_24(ctx->pData, x1, y1, nBytesPerLine);

		for (; y1 <= y2; y1++) {
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_24(i, &Color)
			}
			pScrPtr += nBytesPerLine;
		}
		break;
	}

	return GP_OK;
}

/**
 * Рисование прямоугольника линией единичной толщины с указанной маской
 * @param указатель на графический контекст
 * @param координата по X левой стороны прямоугольника
 * @param координата по Y верхней стороны прямоугольника
 * @param координата по X правой стороны прямоугольника
 * @param координата по Y нижней стороны прямоугольника
 * @param цвет
 * @return код ошибки
 */
int DrawMaskRect(TGraphicContext *ctx, int x1 ,int y1, int x2, int y2, TColor Color) {
	SWAP_IF_NEED()

	char bReorder = ctx->bReorder;
	int nBytesPerLine = ctx->nBytesPerLine;
	int nBytesPerPixel = ctx->nBitsPerPixel / 8;
	char *pLT, *pRT, *pRB, *pLB, *pScrPtr;
	unsigned int nLineMask = ctx->nLineMask;
	unsigned int nCurrentPos = ctx->nCurrentPos;
	unsigned int nLineMaskPos = ctx->nLineMaskPos;

	if (RectAssert(ctx, x1, y1, x2, y2, "DrawMaskRect") != GP_OK) {
		return GP_ERR;
	}

	switch (ctx->nBitsPerPixel) {
	case 8:
		pLT = GET_PIXEL_PTR_8(ctx->pData, x1, y1, nBytesPerLine);
		pRT = GET_PIXEL_PTR_8(ctx->pData, x2, y1, nBytesPerLine);
		pRB = GET_PIXEL_PTR_8(ctx->pData, x2, y2, nBytesPerLine);
		pLB = GET_PIXEL_PTR_8(ctx->pData, x1, y2, nBytesPerLine);

		for (pScrPtr = pLT; pScrPtr != pRT; pScrPtr++) {
			SET_MASKED_PIXEL_8(pScrPtr, &Color)
		}
		for (; pScrPtr != pRB; pScrPtr += nBytesPerLine) {
			SET_MASKED_PIXEL_8(pScrPtr, &Color)
		}
		for (; pScrPtr != pLB; pScrPtr--) {
			SET_MASKED_PIXEL_8(pScrPtr, &Color)
		}
		for (; pScrPtr != pLT; pScrPtr -= nBytesPerLine) {
			SET_MASKED_PIXEL_8(pScrPtr, &Color)
		}
		break;
	case 16:
		pLT = GET_PIXEL_PTR_16(ctx->pData, x1, y1, nBytesPerLine);
		pRT = GET_PIXEL_PTR_16(ctx->pData, x2, y1, nBytesPerLine);
		pRB = GET_PIXEL_PTR_16(ctx->pData, x2, y2, nBytesPerLine);
		pLB = GET_PIXEL_PTR_16(ctx->pData, x1, y2, nBytesPerLine);

		for (pScrPtr = pLT; pScrPtr != pRT; pScrPtr += nBytesPerPixel) {
			SET_MASKED_PIXEL_16(pScrPtr, &Color)
		}
		for (; pScrPtr != pRB; pScrPtr += nBytesPerLine) {
			SET_MASKED_PIXEL_16(pScrPtr, &Color)
		}
		for (; pScrPtr != pLB; pScrPtr -= nBytesPerPixel) {
			SET_MASKED_PIXEL_16(pScrPtr, &Color)
		}
		for (; pScrPtr != pLT; pScrPtr -= nBytesPerLine) {
			SET_MASKED_PIXEL_16(pScrPtr, &Color)
		}
		break;
	case 24:
		pLT = GET_PIXEL_PTR_24(ctx->pData, x1, y1, nBytesPerLine);
		pRT = GET_PIXEL_PTR_24(ctx->pData, x2, y1, nBytesPerLine);
		pRB = GET_PIXEL_PTR_24(ctx->pData, x2, y2, nBytesPerLine);
		pLB = GET_PIXEL_PTR_24(ctx->pData, x1, y2, nBytesPerLine);

		for (pScrPtr = pLT; pScrPtr != pRT; pScrPtr += nBytesPerPixel) {
			SET_MASKED_PIXEL_24(pScrPtr, &Color)
		}
		for (; pScrPtr != pRB; pScrPtr += nBytesPerLine) {
			SET_MASKED_PIXEL_24(pScrPtr, &Color)
		}
		for (; pScrPtr != pLB; pScrPtr -= nBytesPerPixel) {
			SET_MASKED_PIXEL_24(pScrPtr, &Color)
		}
		for (; pScrPtr != pLT; pScrPtr -= nBytesPerLine) {
			SET_MASKED_PIXEL_24(pScrPtr, &Color)
		}
		break;
	}

	return GP_OK;
}

/**
 * Рисование рельефного прямоугольника линией единичной толщины
 * @param указатель на графический контекст
 * @param координата по X левой стороны прямоугольника
 * @param координата по Y верхней стороны прямоугольника
 * @param координата по X правой стороны прямоугольника
 * @param координата по Y нижней стороны прямоугольника
 * @param цвет освещенной части
 * @param цвет не освещенной части
 * @return код ошибки
 */
int DrawReliefRect(TGraphicContext *ctx, int x1 ,int y1, int x2, int y2, TColor LightColor,
		TColor DarkColor, char bSunken) {
	SWAP_IF_NEED()

	char bReorder = ctx->bReorder;
	int nBytesPerLine = ctx->nBytesPerLine;
	int nBytesPerPixel = ctx->nBitsPerPixel / 8;
	int nCnts = (x2 - x1) * nBytesPerPixel;
	int nCnt = nCnts + nBytesPerPixel;
	char *pScrPtr, *pLastPtr;
	char *i;

	if (RectAssert(ctx, x1, y1, x2, y2, "DrawReliefRect") != GP_OK) {
		return GP_ERR;
	}

	switch (ctx->nBitsPerPixel) {
	case 8:
		pScrPtr = GET_PIXEL_PTR_8(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		if (bSunken) {
			for (i = pScrPtr; i < pLastPtr; i++) {
				SET_PIXEL_8(i, &DarkColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_8(pScrPtr, &DarkColor)
				SET_PIXEL_8(pScrPtr + nCnts, &LightColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i++) {
				SET_PIXEL_8(i, &LightColor)
			}
		} else {
			for (i = pScrPtr; i < pLastPtr; i++) {
				SET_PIXEL_8(i, &LightColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_8(pScrPtr, &LightColor)
				SET_PIXEL_8(pScrPtr + nCnts, &DarkColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i++) {
				SET_PIXEL_8(i, &DarkColor)
			}
		}
		break;
	case 16:
		pScrPtr = GET_PIXEL_PTR_16(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		if (bSunken) {
			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_16(i, &DarkColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_16(pScrPtr, &DarkColor)
				SET_PIXEL_16(pScrPtr + nCnts, &LightColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_16(i, &LightColor)
			}
		} else {
			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_16(i, &LightColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_16(pScrPtr, &LightColor)
				SET_PIXEL_16(pScrPtr + nCnts, &DarkColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_16(i, &DarkColor)
			}
		}
		break;
	case 24:
		pScrPtr = GET_PIXEL_PTR_24(ctx->pData, x1, y1, nBytesPerLine);
		pLastPtr = pScrPtr + nCnt;

		if (bSunken) {
			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_24(i, &DarkColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_24(pScrPtr, &DarkColor)
				SET_PIXEL_24(pScrPtr + nCnts, &LightColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_24(i, &LightColor)
			}
		} else {
			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_24(i, &LightColor)
			}
			pScrPtr += nBytesPerLine;

			for (y1++; y1 < y2; y1++) {
				SET_PIXEL_24(pScrPtr, &LightColor)
				SET_PIXEL_24(pScrPtr + nCnts, &DarkColor)
				pScrPtr += nBytesPerLine;
			}
			pLastPtr = pScrPtr + nCnt;

			for (i = pScrPtr; i < pLastPtr; i += nBytesPerPixel) {
				SET_PIXEL_24(i, &DarkColor)
			}
		}
		break;
	}

	return GP_OK;
}
